package com.sias.spring;

import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;

/**
 * @author 吴文杰
 * @version 1.0
 * @since 2023/6/17 13:24
 */
public class SpringAOP {
  public static void main(String[] args) throws NoSuchMethodException {
    // proxyXuanZe();

    // AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    // pointcut.setExpression("execution(* bar())");
    // System.out.println(pointcut.matches(T1.class.getMethod("foo"), T1.class));
    // System.out.println(pointcut.matches(T1.class.getMethod("bar"), T1.class));
    //
    // AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
    // pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
    // System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
    // System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));

    StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
      @Override
      public boolean matches(Method method, Class<?> targetClass) {
        MergedAnnotations annotations = MergedAnnotations.from(method);
        if (annotations.isPresent(Transactional.class)) {
          return true;
        }
        annotations = MergedAnnotations.from(targetClass);
        if (annotations.isPresent(Transactional.class)) {
          return true;
        }
        return false;
      }
    };

    System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
  }

  /**
   * 1.Spring代理选择
   */
  private static void proxyXuanZe() {
    // 1.切点
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression("execution(* foo())");
    // 2.通知
    MethodInterceptor advice = invocation -> {
      System.out.println("before");
      Object result = invocation.proceed();
      System.out.println("after");
      return result;
    };
    // 3.切面
    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    // 4.代理
    /*
      a. proxyTargetClass = false;  目标实现了接口，用jdk实现
      b. proxyTargetClass = false;  目标没有实现接口，用cglib实现
      c. proxyTargetClass = true;   总是使用cglib实现
     */
    Target2 target1 = new Target2();
    ProxyFactory factory = new ProxyFactory();
    factory.setTarget(target1);
    factory.addAdvisor(advisor);
    factory.setInterfaces(target1.getClass().getInterfaces());
    // factory.setProxyTargetClass(true);

    Target2 proxy = (Target2) factory.getProxy();
    System.out.println(proxy.getClass());
    proxy.foo();
    proxy.bar();
  }


  interface I1 {
    void foo();

    void bar();
  }

  @Transactional
  interface I3 {
    public void foo();
  }

  static class T3 implements I3 {

    @Override
    public void foo() {

    }
  }

  @Transactional
  static class T2 {
    public void foo() {
    }
  }

  static class T1 {
    @Transactional
    public void foo() {
    }

    public void bar() {
    }
  }

  static class Target1 implements I1 {

    @Override
    public void foo() {
      System.out.println("target1 foo()");
    }

    @Override
    public void bar() {
      System.out.println("target1 bar()");
    }
  }

  static class Target2 {
    public void foo() {
      System.out.println("target2 foo()");
    }

    public void bar() {
      System.out.println("target2 bar()");
    }
  }


  @Aspect
  static class MyAspect {
    @Before("execution(* foo())")
    public void before() {
      System.out.println("前置增强");
    }
  }
}
